間距、間距、間距!
在討論今天的主題之前,我們先來看看下面這張圖:
第一張圖的情境,有可能是在介面上我們試圖要做一些不可逆的操作,例如你要去刪除某篇文章,系統跳出一個確認訊息避免你誤按,跟你 double check 你要做的行為是否是你確定要這麼做的。
第二章圖的情境,有可能是我們要在某個部落格上發表文章,或是編輯文章,做完所有編輯行為之後,要將內容送出。
類似像這樣的介面我們生活中隨處可見。當然,在我們的工作上,你也有很高的機率會需要設計這樣的介面。
今天我們要討論的主題是,像這樣的介面共同的部分,是在下面有一顆按鈕讓我們做確認。那這顆按鈕有什麼特別的呢?
當然,我們今天如果只有一顆按鈕,確實是沒有什麼特別的。但一般來說像這樣的介面,通常會有兩顆按鈕,我選一張圖來舉例:
.actions {
display: flex;
}
<div class="actions">
<button class="button outlined">Cancel</button>
<button class="button contained">Confirm</button>
</div>
我們花了一番功夫,終於讓按鈕可以做水平排版了之後,喘完一口氣,卻發現上面這兩顆按鈕黏太近了,要怎麼辦呢?
黏太近的話當然就是把他隔開嘛,這還用說?但仔細想想,「你要用什麼方法把他隔開呢?」。
這就是我們今天要討論的主題了。
我們來看看今天能夠想到幾種能達到同樣效果的方法,並且一起來分析他的優劣吧~
inline-style 是一個很直覺得做法,看到哪裡缺什麼就在那裡加什麼:
<div class="actions">
<button class="button outlined">Cancel</button>
<button class="button contained" style="margin-left: 12px">Confirm</button>
</div>
當然這樣的做法是一定會奏效的,只是我們會需要來討論這樣到底有什麼隱憂。
其中一個部分是,使用 inline-style 雖然方便,但是他卻沒有辦法實作如 media-query、hover 等特殊 CSS 效果。
所以如果今天的設計稿是,在寬螢幕的呈現方式需要兩顆 button 距離為 20px,變成手機版窄螢幕的時候需要調整為 12px。那這樣 inline-style 就會有點困擾,我們可能就會需要多寫一段 javascript 來做適應性的調整。
另一個隱憂是樣式權重的問題:
// 最左邊的權重最高,最右邊的權重最低
!important > inline-style > ID > Class/psuedo-class(偽類)/attribute(屬性選擇器) > Element
如果我們程式碼到處都用了 inline-style,那很有可能你會發現寫了某段 CSS 之後卻沒有出現效果,因為偷偷在某個你已經忘記了的地方有權重更高的 CSS 壓過你目前的 CSS。這時候你要回去找這段在做怪的 CSS 時,就會發現很頭大了。有可能他會很難找,也有可能你改了他之後,其他地方的樣式也會被影響到。
那既然上述例子的做法不太推薦,那把 inline-style 拿出來放在 class 裡面如何呢?
.first-button {
${buttonStyle}
}
.not-first-button {
${buttonStyle}
margin-left: 12px;
}
<div class="actions">
<button class="outlined first-button">Cancel</button>
<button class="contained not-first-button" style="margin-left: 12px">Confirm</button>
</div>
我們在 class 裡面可以寫 media-query,另外 class 的權重也比 inline-style 低,解決了這兩個問題。
但是用這種寫法仍然無法擺脫一個問題,就是感覺有點過於 hard code 了,寫法上不太有彈性。舉例來說,假設還有其他的 button 接續在後面,那是不是每一個 button 都需要加上這個樣式呢?
你需要很小心,不能漏掉一個。如果這段程式碼被修修改改,特別是被不同的人接手修改,那很有可能會被改到漏掉。
<div class="actions">
<button class="outlined first-button">Cancel</button>
<button class="contained not-first-button" style="margin-left: 12px">Confirm</button>
<button class="contained not-first-button" style="margin-left: 12px">Edit</button>
<button class="contained not-first-button" style="margin-left: 12px">Delete</button>
</div>
上面兩種方法有剛剛提到的缺點。另外我們再深入一點思考,「外間距是屬於 button 上面的屬性嗎?」
換句話來說,如果 button 沒有帶外間距的樣式,他還是不是一顆完整的 button 呢?答案當然是肯定的。
我們可以這樣理解,button 元件本身其實不需要外間距,會有這個外間距的需要,是因為這裡有一群 button 擠在這裡,所以需要間距把彼此隔開,是因為這個「群體」所以才需要間距,撇開群體不談,button 本身壓根就不需要外間距。
既然我們得出這個結論了,那我們就把間距的控制從 button 裡面大膽的抽出來吧!我們應該把他放在外容器上,就是包覆這個 button 群體的父元素。
[經驗分享]
如果以元件化的角度來看,我們在系統上必定會有這個系統共用的 button 元件,這樣的共用元件會被放在各種地方,不只是我們今天所舉的那兩種情境。那假設這個 button 被放在另外一個情境,但這個情境卻不需要間距,那是不是很尷尬呢?我們還需要特別寫一些 CSS 把這些多餘的樣式拿掉,顯得特別麻煩,可讀性也會降低,今天你自己看得懂,別人來看就不知道你的用意。
所以以上述的狀況來考量的話,我不太建議把跟 button 元件本身無關的間距直接放在 button 上。
使用相鄰兄弟選取器(adjacent-sibling combinator)
如果需要選擇緊接在另一个元素後的元素,而且二者有相同的父元素,可以使用相鄰兄弟選取器(Adjacent sibling selector)。
.button + .button {
margin-left: 12px;
}
使用頭尾選取器(:first-child)
:first-child
從字面上來解釋的話,就是選到第一個子物件,那我們想要做的事情是,「除了第一個 button 之外,其他後面的 button 都要加上左邊的外間距」。
.actions :not(:first-child) {
margin-left: 12px;
}
使用 Flexbox 佈局
在做元件的水平、垂直佈局時,Flexbox 是很常用的佈局方法。在處理間距這件事情上面,Flexbox 也提供了很棒的實作,可以直接用 gap 來定義垂直方向及水平方向的間距:
.flex-container {
display: flex;
flex-wrap: wrap;
column-gap: 12px;
row-gap: 20px;
}
但是在使用 Flexbox gap 會需要特別留意在瀏覽器版本的支援度,Safari 在 14.1 之前是不支援 Flexbox gap 的喔!
加上間距之後就能夠順利把 button 隔開了:
今天我們討論了 button 水平排版的間距,並且討論幾種能達到同樣效果的方法,並且一起來分析他的優劣:
今天是以 button 水平佈局情境為例來說明間距的處理,但今天討論的內容不限於 button 元件喔!